package org.jeecg.modules.system.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import javax.annotation.Resource;

import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.cache.UserPermissionCache;
import org.jeecg.common.constant.CacheConstant;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.MD5Util;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.config.IscConfig;
import org.jeecg.modules.system.entity.SysPermission;
import org.jeecg.modules.system.entity.SysPermissionDataRule;
import org.jeecg.modules.system.entity.SysUser;
import org.jeecg.modules.system.mapper.SysDepartPermissionMapper;
import org.jeecg.modules.system.mapper.SysDepartRolePermissionMapper;
import org.jeecg.modules.system.mapper.SysPermissionMapper;
import org.jeecg.modules.system.mapper.SysRolePermissionMapper;
import org.jeecg.modules.system.mapper.SysUserMapper;
import org.jeecg.modules.system.model.TreeModel;
import org.jeecg.modules.system.service.IPermissionService;
import org.jeecg.modules.system.service.ISysPermissionDataRuleService;
import org.jeecg.modules.system.util.IscUtil;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sgcc.isc.core.orm.complex.FunctionNode;
import com.sgcc.isc.core.orm.complex.FunctionTree;
import com.sgcc.isc.core.orm.resource.Function;
import com.sgcc.isc.core.orm.resource.PermissionObject;
import com.sgcc.isc.service.adapter.factory.AdapterFactory;
import com.sgcc.isc.service.adapter.helper.IResourceService;

import lombok.extern.slf4j.Slf4j;

/**
 * <p>
 * 菜单权限表 服务实现类
 * </p>
 *
 * @Author scott
 * @since 2018-12-21
 */
@Slf4j
@Service
@ConditionalOnProperty(name = "mesh.login.auth-impl", havingValue = "isc")
public class IscPermissionServiceImpl extends ServiceImpl<SysPermissionMapper, SysPermission> implements IPermissionService {

	@Resource
	private SysPermissionMapper sysPermissionMapper;

	@Resource
	private ISysPermissionDataRuleService permissionDataRuleService;

	@Resource
	private SysRolePermissionMapper sysRolePermissionMapper;

	@Resource
	private SysDepartPermissionMapper sysDepartPermissionMapper;

	@Resource
	private SysDepartRolePermissionMapper sysDepartRolePermissionMapper;
	@Resource
	private SysUserMapper sysUserMapper;

	@Override
	public List<TreeModel> queryListByParentId(String parentId) {
		return sysPermissionMapper.queryListByParentId(parentId);
	}

	/**
	 * 真实删除
	 */
	@Override
	@Transactional
	@CacheEvict(value = CacheConstant.SYS_DATA_PERMISSIONS_CACHE, allEntries = true)
	public void deletePermission(String id) throws JeecgBootException {
		IResourceService resourceService = AdapterFactory.getResourceService();
		try {
			// isc删除
			resourceService.delFuncByFuncId(id);

			SysPermission sysPermission = this.getById(id);
			if (sysPermission == null) {
				throw new JeecgBootException("未找到菜单信息");
			}
			String pid = sysPermission.getParentId();
			if (oConvertUtils.isNotEmpty(pid)) {
				int count = this.count(new QueryWrapper<SysPermission>().lambda().eq(SysPermission::getParentId, pid));
				if (count == 1) {
					// 若父节点无其他子节点，则该父节点是叶子节点
					this.sysPermissionMapper.setMenuLeaf(pid, 1);
				}
			}
			sysPermissionMapper.deleteById(id);
			// 该节点可能是子节点但也可能是其它节点的父节点,所以需要级联删除
			this.removeChildrenBy(sysPermission.getId());
			// 关联删除
			Map map = new HashMap<>();
			map.put("permission_id", id);
			// 删除数据规则
			this.deletePermRuleByPermId(id);
			// 删除角色授权表
			sysRolePermissionMapper.deleteByMap(map);
			// 删除部门权限表
			sysDepartPermissionMapper.deleteByMap(map);
			// 删除部门角色授权
			sysDepartRolePermissionMapper.deleteByMap(map);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 根据父id删除其关联的子节点数据
	 *
	 * @return
	 */
	public void removeChildrenBy(String parentId) {
		LambdaQueryWrapper<SysPermission> query = new LambdaQueryWrapper<>();
		// 封装查询条件parentId为主键,
		query.eq(SysPermission::getParentId, parentId);
		// 查出该主键下的所有子级
		List<SysPermission> permissionList = this.list(query);
		if (permissionList != null && permissionList.size() > 0) {
			String id = ""; // id
			int num = 0; // 查出的子级数量
			// 如果查出的集合不为空, 则先删除所有
			this.remove(query);
			// 再遍历刚才查出的集合, 根据每个对象,查找其是否仍有子级
			for (int i = 0, len = permissionList.size(); i < len; i++) {
				id = permissionList.get(i).getId();
				Map map = new HashMap<>();
				map.put("permission_id", id);
				// 删除数据规则
				this.deletePermRuleByPermId(id);
				// 删除角色授权表
				sysRolePermissionMapper.deleteByMap(map);
				// 删除部门权限表
				sysDepartPermissionMapper.deleteByMap(map);
				// 删除部门角色授权
				sysDepartRolePermissionMapper.deleteByMap(map);
				num = this.count(new LambdaQueryWrapper<SysPermission>().eq(SysPermission::getParentId, id));
				// 如果有, 则递归
				if (num > 0) {
					this.removeChildrenBy(id);
				}
			}
		}
	}

	/**
	 * 逻辑删除
	 */
	@Override
	@CacheEvict(value = CacheConstant.SYS_DATA_PERMISSIONS_CACHE, allEntries = true)
	// @CacheEvict(value =
	// CacheConstant.SYS_DATA_PERMISSIONS_CACHE,allEntries=true,condition="#sysPermission.menuType==2")
	public void deletePermissionLogical(String id) throws JeecgBootException {
		IResourceService resourceService = AdapterFactory.getResourceService();
		try {
			SysPermission sysPermission = this.getById(id);
			if (sysPermission == null) {
				throw new JeecgBootException("未找到菜单信息");
			}
			// isc删除
			resourceService.delFuncByFuncId(id);

			String pid = sysPermission.getParentId();
			int count = this.count(new QueryWrapper<SysPermission>().lambda().eq(SysPermission::getParentId, pid));
			if (count == 1) {
				// 若父节点无其他子节点，则该父节点是叶子节点
				this.sysPermissionMapper.setMenuLeaf(pid, 1);
			}
			sysPermission.setDelFlag(1);
			this.updateById(sysPermission);
		} catch (Exception e) {
			throw new JeecgBootException("删除失败");
		}
	}

	@Override
	@CacheEvict(value = CacheConstant.SYS_DATA_PERMISSIONS_CACHE, allEntries = true)
	public void addPermission(SysPermission sysPermission) throws JeecgBootException {
		IResourceService resourceService = AdapterFactory.getResourceService();
		// ----------------------------------------------------------------------
		// 判断是否是一级菜单，是的话清空父菜单

		if (CommonConstant.MENU_TYPE_0.equals(sysPermission.getMenuType())) {
			sysPermission.setParentId(null);
		}
		// ----------------------------------------------------------------------
		String pid = sysPermission.getParentId();
		if (oConvertUtils.isNotEmpty(pid)) {
			// 设置父节点不为叶子节点
			this.sysPermissionMapper.setMenuLeaf(pid, 0);
		}
		sysPermission.setCreateTime(new Date());
		sysPermission.setDelFlag(0);
		sysPermission.setLeaf(true);

		// 添加菜单到isc
		// 验证父权限是否存在
		if (new LambdaQueryWrapper<SysPermission>().eq(SysPermission::getId, sysPermission.getParentId()) == null) {
			throw new JeecgBootException("未找到父权限");
		}
		Function func = new Function();
		IscUtil.sysPermissionToFunction(sysPermission, func);

		List<PermissionObject> permObjects = new ArrayList<>();
		LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
		try {
			resourceService.addFunc(loginUser.getId(), sysPermission.getParentId(), func, permObjects);
			this.save(sysPermission);
		} catch (Exception e) {
			throw new JeecgBootException("添加菜单出错");
		}
	}

	@Override
	@CacheEvict(value = CacheConstant.SYS_DATA_PERMISSIONS_CACHE, allEntries = true)
	public void editPermission(SysPermission sysPermission) throws JeecgBootException {
		SysPermission p = this.getById(sysPermission.getId());

		if (p == null) {
			throw new JeecgBootException("未找到菜单信息");
		} else {
			sysPermission.setUpdateTime(new Date());
			// ----------------------------------------------------------------------
			// Step1.判断是否是一级菜单，是的话清空父菜单ID
			if (CommonConstant.MENU_TYPE_0.equals(sysPermission.getMenuType())) {
				sysPermission.setParentId("");
			}
			// Step2.判断菜单下级是否有菜单，无则设置为叶子节点
			int count = this.count(new QueryWrapper<SysPermission>().lambda().eq(SysPermission::getParentId, sysPermission.getId()));
			if (count == 0) {
				sysPermission.setLeaf(true);
			}
			// ----------------------------------------------------------------------
			this.updateById(sysPermission);

			// 如果当前菜单的父菜单变了，则需要修改新父菜单和老父菜单的，叶子节点状态
			String pid = sysPermission.getParentId();
			if ((oConvertUtils.isNotEmpty(pid) && !pid.equals(p.getParentId())) || oConvertUtils.isEmpty(pid) && oConvertUtils.isNotEmpty(p.getParentId())) {
				// a.设置新的父菜单不为叶子节点
				this.sysPermissionMapper.setMenuLeaf(pid, 0);
				// b.判断老的菜单下是否还有其他子菜单，没有的话则设置为叶子节点
				int cc = this.count(new QueryWrapper<SysPermission>().lambda().eq(SysPermission::getParentId, p.getParentId()));
				if (cc == 0) {
					if (oConvertUtils.isNotEmpty(p.getParentId())) {
						this.sysPermissionMapper.setMenuLeaf(p.getParentId(), 1);
					}
				}

			}
		}

	}

	@Override
	public List<SysPermission> queryByUser(String username) {
		return this.sysPermissionMapper.queryByUser(username);
	}

	/**
	 * 根据permissionId删除其关联的SysPermissionDataRule表中的数据
	 */
	@Override
	public void deletePermRuleByPermId(String id) {
		try {
			LambdaQueryWrapper<SysPermissionDataRule> query = new LambdaQueryWrapper<>();
			query.eq(SysPermissionDataRule::getPermissionId, id);
			int countValue = this.permissionDataRuleService.count(query);
			if (countValue > 0) {
				this.permissionDataRuleService.remove(query);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 获取模糊匹配规则的数据权限URL
	 */
	@Override
	@Cacheable(value = CacheConstant.SYS_DATA_PERMISSIONS_CACHE)
	public List<String> queryPermissionUrlWithStar() {
		return this.baseMapper.queryPermissionUrlWithStar();
	}

	@Override
	public boolean hasPermission(String username, SysPermission sysPermission) {
		int count = baseMapper.queryCountByUsername(username, sysPermission);
		if (count > 0) {
			return true;
		} else {
			return false;
		}
	}

	@Override
	public boolean hasPermission(String username, String url) {
		SysPermission sysPermission = new SysPermission();
		sysPermission.setUrl(url);
		int count = baseMapper.queryCountByUsername(username, sysPermission);
		if (count > 0) {
			return true;
		} else {
			return false;
		}
	}

	@Override
	public Result<?> getUserPermissionByToken(String token) {
		Result<JSONObject> result = new Result<JSONObject>();
		try {
			if (oConvertUtils.isEmpty(token)) {
				return Result.error("TOKEN不允许为空！");
			}
			String username = JwtUtil.getUsername(token);

			log.info("getUserPermissionByToken username:" + username);
			JSONObject jsonCache = UserPermissionCache.getByUsername(username);
			log.info("getUserPermissionByToken username:" + username + "   jsonCache:" + jsonCache);
			if (jsonCache == null) {
				log.info("getUserPermissionByToken username:" + username + "   jsonCache is null and get cache");
				jsonCache = getPermissionByUsername(username);
				log.info("getUserPermissionByToken username:" + username + "  get jsonCache, now:" + jsonCache);
				if (jsonCache == null) {
					return Result.error("用户不存在！");
				}
				// 保存内存
				UserPermissionCache.put(username, jsonCache);
			}

			result.setResult(jsonCache);
			result.success("查询成功");
		} catch (Exception e) {
			result.error500("查询失败");
			log.error(e.getMessage(), e);
		}
		return result;
	}

	@Override
	public JSONObject getPermissionByUsername(String username) throws Exception {
		IResourceService resourceService = AdapterFactory.getResourceService();

		JSONObject jsonCache = new JSONObject();
		SysUser sysUser = sysUserMapper.getUserByName(username);
		if (sysUser == null) {
			return null;
		}
		// 从isc获取菜单
		String systemId = IscConfig.getIscAppId();
		FunctionTree funcTree = resourceService.getFuncTree(sysUser.getId(), systemId, null);
		if (funcTree==null){
			log.info("用户没有访问此业务的权限");
			return jsonCache;
		}
		List<FunctionNode> functionNode = funcTree.getFuncNode();

		JSONArray menujsonArray = new JSONArray();
		this.getPermissionJsonArray(menujsonArray, functionNode, null);
		JSONArray authjsonArray = new JSONArray();
		this.getAuthJsonArray(authjsonArray, functionNode);

		// 查询所有的权限
		JSONArray allauthjsonArray = new JSONArray();
		this.getAllAuthJsonArray(allauthjsonArray, functionNode);
		// 路由菜单
		jsonCache.put("menu", menujsonArray);
		// 按钮权限（用户拥有的权限集合）
		jsonCache.put("auth", authjsonArray);
		// 全部权限配置集合（按钮权限，访问权限）
		jsonCache.put("allAuth", allauthjsonArray);

		// 同步菜单到本地数据库
		List<SysPermission> permissions = new ArrayList<>();
		syncPermissionToLocal(permissions, functionNode);
		this.saveOrUpdateBatch(permissions);
		return jsonCache;
	}

	private void syncPermissionToLocal(List<SysPermission> permissions, List<FunctionNode> functionNodes) {
		for (FunctionNode functionNode : functionNodes) {
			SysPermission permission = IscUtil.iscFunctionToSysPermission(functionNode.getCurrentNode());
			permissions.add(permission);
			// 如果该功能下存在子功能
			if (functionNode.getNextNode().size() > 0) {
				syncPermissionToLocal(permissions, functionNode.getNextNode());
			}
		}
	}

	@Override
	public void getPermissionJsonArray(JSONArray jsonArray, List<?> metaList, JSONObject parentJson) {
		for (FunctionNode functionNode : (List<FunctionNode>) metaList) {
			SysPermission permission = IscUtil.iscFunctionToSysPermission(functionNode.getCurrentNode());
			if (permission.getMenuType() == null) {
				continue;
			}
			String tempPid = permission.getParentId();
			JSONObject json = getPermissionJsonObject(permission);
			if (json == null) {
				continue;
			}
			if (parentJson == null) {
				jsonArray.add(json);
				// 如果该功能下存在子功能
				if (functionNode.getNextNode().size() > 0) {
					getPermissionJsonArray(jsonArray, functionNode.getNextNode(), json);
				}
			} else if (tempPid.equals(parentJson.getString("id"))) {
				// 不会进到这里的
				if (permission.getMenuType().equals(CommonConstant.MENU_TYPE_2)) {
					JSONObject metaJson = parentJson.getJSONObject("meta");
					if (metaJson.containsKey("permissionList")) {
						metaJson.getJSONArray("permissionList").add(json);
					} else {
						JSONArray permissionList = new JSONArray();
						permissionList.add(json);
						metaJson.put("permissionList", permissionList);
					}
					// 如果是菜单
				} else if (permission.getMenuType().equals(CommonConstant.MENU_TYPE_0)) {
					if (parentJson.containsKey("children")) {
						parentJson.getJSONArray("children").add(json);
					} else {
						JSONArray children = new JSONArray();
						children.add(json);
						parentJson.put("children", children);
					}

					// 如果该功能下存在子功能
					if (functionNode.getNextNode().size() > 0) {
						getPermissionJsonArray(jsonArray, functionNode.getNextNode(), json);
					}
				}
			}

		}
	}

	@Override
	public void getAuthJsonArray(JSONArray jsonArray, List<?> metaList) {
		for (FunctionNode functionNode : (List<FunctionNode>) metaList) {
			SysPermission permission = IscUtil.iscFunctionToSysPermission(functionNode.getCurrentNode());
			if (permission.getMenuType() == null) {
				continue;
			}
			JSONObject json = null;
			if (permission.getMenuType().equals(CommonConstant.MENU_TYPE_2)) {
				json = new JSONObject();
				json.put("action", permission.getPerms());
				json.put("type", null);
				json.put("describe", permission.getName());
				jsonArray.add(json);
			}
			// 如果该功能下存在子功能
			if (functionNode.getNextNode().size() > 0) {
				getAuthJsonArray(jsonArray, functionNode.getNextNode());
			}
		}
	}

	@Override
	public void getAllAuthJsonArray(JSONArray jsonArray, List<?> allList) {
		JSONObject json = null;
		for (FunctionNode functionNode : (List<FunctionNode>) allList) {
			SysPermission permission = IscUtil.iscFunctionToSysPermission(functionNode.getCurrentNode());
			if (Objects.equals(permission.getDelFlag(), CommonConstant.DEL_FLAG_0) && Objects.equals(permission.getMenuType(), CommonConstant.MENU_TYPE_2)) {
				json = new JSONObject();
				json.put("action", permission.getPerms());
				json.put("status", permission.getStatus());
				json.put("type", permission.getPermsType());
				json.put("describe", permission.getName());
				jsonArray.add(json);
			}
			// 如果该功能下存在子功能
			if (functionNode.getNextNode().size() > 0) {
				getAllAuthJsonArray(jsonArray, functionNode.getNextNode());
			}
		}
	}

	// 不能返回的信息都设置为null
	@Override
	public JSONObject getPermissionJsonObject(SysPermission permission) {
		JSONObject json = new JSONObject();

		// 如果是功能或界面，不返回
		if (permission.getMenuType().equals(CommonConstant.MENU_TYPE_2) || permission.getMenuType().equals(CommonConstant.MENU_TYPE_1)) {
			// json.put("action", permission.getPerms());
			// json.put("type", permission.getPermsType());
			// json.put("describe", permission.getName());
			return null;
		} else if (permission.getMenuType().equals(CommonConstant.MENU_TYPE_0)) {
			json.put("id", permission.getId());
			json.put("route", null);

			if (isWWWHttpUrl(permission.getUrl())) {
				json.put("path", MD5Util.MD5Encode(permission.getUrl(), "utf-8"));
			} else {
				json.put("path", permission.getUrl());
			}

			// 重要规则：路由name (通过URL生成路由name,路由name供前端开发，页面跳转使用)
			/*
			 * if (oConvertUtils.isNotEmpty(permission.getComponentName())) {
			 * json.put("name", permission.getComponentName()); } else {
			 * json.put("name", urlToRouteName(permission.getUrl())); }
			 */
			json.put("name", urlToRouteName(permission.getUrl()));

			json.put("hidden", null);
			json.put("alwaysShow", null);
			json.put("component", null);

			JSONObject meta = new JSONObject();
			meta.put("keepAlive", null);
			meta.put("internalOrExternal", null);

			meta.put("title", permission.getName());
			if (oConvertUtils.isEmpty(permission.getParentId())) {
				// 一级菜单跳转地址
				json.put("redirect", null);
				meta.put("icon", null);
			} else {
				meta.put("icon", null);
			}
			if (isWWWHttpUrl(permission.getUrl())) {
				meta.put("url", permission.getUrl());
			}
			json.put("meta", meta);
		}

		return json;
	}

	@Override
	public boolean isWWWHttpUrl(String url) {
		return url != null && (url.startsWith("http://") || url.startsWith("https://") || url.startsWith("{{"));
	}

	@Override
	public String urlToRouteName(String url) {
		if (oConvertUtils.isNotEmpty(url)) {
			if (url.startsWith("/")) {
				url = url.substring(1);
			}
			url = url.replace("/", "-");

			// 特殊标记
			url = url.replace(":", "@");
			return url;
		} else {
			return null;
		}
	}

}
